package com.eastfair.imaster.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.eastfair.annotation.base.LanguageTypeEnum;
import com.eastfair.annotation.base.SubjectTypeEnum;
import com.eastfair.boot.request.PageParams;
import com.eastfair.boot.service.SuperCacheServiceImpl;
import com.eastfair.cache.operations.NewsCacheKeyBuilder;
import com.eastfair.constant.BusinessConstant;
import com.eastfair.constant.EntityAttributeString;
import com.eastfair.constant.SortConstant;
import com.eastfair.core.base.R;
import com.eastfair.core.context.ContextUtil;
import com.eastfair.core.model.CacheKeyBuilder;
import com.eastfair.core.snowflake.DefaultUidGenerator;
import com.eastfair.core.utils.ConvertUtil;
import com.eastfair.core.utils.StringUtils;
import com.eastfair.imaster.dao.NewsMapper;
import com.eastfair.imaster.dto.NewsDTO;
import com.eastfair.imaster.dto.NewsPageQuery;
import com.eastfair.imaster.entity.News;
import com.eastfair.imaster.service.NewsService;
import com.eastfair.imaster.vo.NewsVO;
import com.eastfair.projectcore.api.ProjectCoreServiceFeign;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;

/**
 * <p>
 * 业务实现类
 *
 * </p>
 *
 * @author zjn
 * @date 2021-08-31
 */
@Slf4j
@Service
@DS("#thread.tenant")
public class NewsServiceImpl extends SuperCacheServiceImpl<NewsMapper, News> implements NewsService {

    @Autowired
    ProjectCoreServiceFeign projectCoreServiceFeign;
    @Autowired
    private NewsMapper newsMapper;

    @Override
    protected CacheKeyBuilder cacheKeyBuilder() {
        return new NewsCacheKeyBuilder();
    }

    /**************sharding 分库分表操作 *****************/
    @Override
    public boolean updateByIdOfShard(News entity) {
        UpdateWrapper<News> updateWrapper = new UpdateWrapper<>();
        //可将指定字段更新为null
        updateWrapper.lambda().eq(News::getProjectId, entity.getProjectId()).eq(News::getId, entity.getId());
        entity.setProjectId(null);
        //删除缓存
        delCache(entity);
        return SqlHelper.retBool(this.getBaseMapper().update(entity, updateWrapper));
    }

    /**
     * 处理新增相关处理
     *
     * @param model 实体
     * @return 是否成功
     */
    @Override
    protected R<Boolean> handlerSave(News model) {
        //雪花ID
        model.setId(DefaultUidGenerator.getUidOfProjectId(ContextUtil.getProjectId()));
        model.setProjectId(ContextUtil.getProjectId());

        // 设置序号改变修改时间
        convertSequence(model, null);
        return R.successDef();
    }

    @Override
    public Boolean deleteById(Long id) {
        News news = new News();
        news.setId(id);
        news.setProjectId(ContextUtil.getProjectId());
        news.setIsDeleted(BusinessConstant.DELETE_YES);
        return updateByIdOfShard(news);
    }

    @Override
    public Boolean updateById(NewsDTO newsDTO) {
        NewsVO newsVO = queryById(newsDTO.getId());
        if (newsVO == null) {
            return false;
        }

        News news = BeanUtil.copyProperties(newsDTO, News.class);
        news.setProjectId(ContextUtil.getProjectId());

        // 如果序号改变，设置序号改变修改时间
        convertSequence(news, newsVO);

        return updateByIdOfShard(news);
    }

    @Override
    public NewsVO queryById(Long id) {
        QueryWrapper<News> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(News::getProjectId, ContextUtil.getProjectId()).eq(News::getId, id);
        News news = getOne(wrapper, false);
        return BeanUtil.copyProperties(news, NewsVO.class);
    }

    @Override
    public Page<NewsVO> queryPageList(PageParams<NewsPageQuery> params) {
        QueryWrapper<News> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(News::getProjectId, ContextUtil.getProjectId())
                .eq(News::getIsDeleted, BusinessConstant.DELETE_NO)
                .orderByAsc(News::getSequence).orderByDesc(News::getSequenceUpdateTime)
                .select(News.class, info -> !info.getColumn().equals(EntityAttributeString.CONTENT));

        // 条件拼接
        if (StringUtils.isBlank(params.getModel().getSubjectType())) {
            wrapper.lambda().eq(News::getSubjectType, SubjectTypeEnum.get(params.getModel().getSubjectType()).name());
        }
        if (StringUtils.isBlank(params.getModel().getLanguageType())) {
            wrapper.lambda().eq(News::getSubjectType, LanguageTypeEnum.get(params.getModel().getLanguageType()).name());
        }
        if (params.getModel().getIsEnabled() != null) {
            wrapper.lambda().eq(News::getIsEnabled, params.getModel().getIsEnabled());
        }
        if (StringUtils.isBlank(params.getModel().getType())) {
            wrapper.lambda().eq(News::getType, params.getModel().getType());
        }
        if (StringUtils.isBlank(params.getModel().getTitle())) {
            wrapper.lambda().like(News::getType, params.getModel().getTitle());
        }

        Page<News> newsPage = page(params.buildPage(), wrapper);
        return ConvertUtil.convertPage(newsPage, NewsVO.class);
    }

    @Override
    public List<NewsVO> queryList(NewsDTO newsDTO) {
        QueryWrapper<News> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(News::getProjectId, ContextUtil.getProjectId())
                .eq(News::getIsDeleted, BusinessConstant.DELETE_NO)
                .eq(News::getIsEnabled, BusinessConstant.ENABLE_YES)
                .eq(News::getSubjectType, ContextUtil.getSubjectType())
                .eq(News::getLanguageType, ContextUtil.getLanguageType())
                .orderByAsc(News::getSequence).orderByDesc(News::getSequenceUpdateTime)
                .select(News.class, info -> !info.getColumn().equals(EntityAttributeString.CONTENT));
        List<News> result = list(wrapper);
        return ConvertUtil.convertList(result, NewsVO.class);
    }

    /**
     * 如果序号更新，则更新序号更新时间
     *
     * @param news   新实体
     * @param newsVO 老实体
     */
    private void convertSequence(News news, NewsVO newsVO) {
        if (news.getSequence() == null) {
            news.setSequence(SortConstant.DEFUALT);
        }
        if (newsVO == null) {
            news.setSequenceUpdateTime(LocalDateTime.now());
        } else if (!news.getSequence().equals(newsVO.getSequence())) {
            news.setSequenceUpdateTime(LocalDateTime.now());
        }
    }

    /**
     * 处理排序
     *
     * @param model 新增/修改的实体
     * @return 新增/修改的实体
     */
    private void handlerSequence(News model) {
        if (SortConstant.DEFUALT <= model.getSequence() || BusinessConstant.DELETE_YES == model.getIsDeleted()) {
            // 大于等于默认排序的不处理,删除不处理
            return;
        }

        // 新增
        QueryWrapper<News> queryWrapper = new QueryWrapper<>();
        if (model.getId() == null) {
            // 排序为空，设置默认排序
            if (model.getSequence() == null) {
                return;
            }

            // 序号未被占用，可以直接使用
            queryWrapper.lambda().eq(News::getProjectId, ContextUtil.getProjectId()).eq(News::getIsDeleted, BusinessConstant.DELETE_NO)
                    .eq(News::getSequence, model.getSequence());
            News news = getOne(queryWrapper, false);
            if (news == null) {
                return;
            }

            // 序号被占用，将 原大于等于当前序号且小于默认序号 的值都+1
            newsMapper.updateSequenceBetween(ContextUtil.getProjectId(), model.getSequence(), SortConstant.DEFUALT);
        } else {
            // 修改
            // 排序为空，设置默认排序
            if (model.getSequence() == null) {
                model.setSequence(SortConstant.DEFUALT);
                return;
            }

            queryWrapper.lambda().eq(News::getProjectId, ContextUtil.getProjectId()).eq(News::getIsDeleted, BusinessConstant.DELETE_NO)
                    .eq(News::getSequence, model.getSequence());
            News news = getOne(queryWrapper, false);
            //序号未被占用，可以直接使用
            if (news == null) {
                return;
            }

            // 序号被占用
            queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda().eq(News::getProjectId, ContextUtil.getProjectId())
                    .eq(News::getId, model.getId());
            News oldModel = getOne(queryWrapper);
            if (model.getSequence() > oldModel.getSequence()) {
                // 新序号大于老序号  将 原大于等于新序号且小于默认序号 的值都+1
                newsMapper.updateSequenceBetween(ContextUtil.getProjectId(), model.getSequence(), SortConstant.DEFUALT);
            }
            if (model.getSequence() < oldModel.getSequence()) {
                // 新序号小于老序号 将 原大于等于新序号且小于老序号 的值都+1
                newsMapper.updateSequenceBetween(ContextUtil.getProjectId(), model.getSequence(), oldModel.getSequence());
            }
        }
    }

}
